package controller

import (
	syscontext "context"
	"encoding/json"
	"github.com/spf13/viper"
	"go-beego-api/app/models"
	modelDb "go-beego-api/app/models/db"
	redis2 "go-beego-api/app/models/redis"
	"go-beego-api/component/ck_log"
	"go-beego-api/component/ck_validate"
	"go-beego-api/component/redis"
	"go-beego-api/exception"
	"net/http"
	"strings"
	"time"

	"github.com/astaxie/beego"
	"github.com/astaxie/beego/logs"
	"github.com/dgrijalva/jwt-go"
	jsoniter "github.com/json-iterator/go"
	"go.uber.org/zap"
)

// BaseController 控制器基础 针对两个部分的数据
// 1. 支持个人账号的登录
// 2. 支持个人选择企业进行登录
type BaseController struct {
	IsInitToken      bool //是否初始化过token
	UserId           int
	UserInfo         *modelDb.User
	SID              string // nr_user.user_sid
	Token            string
	ParseToken       string //用户企业微信回调时，判断用户
	LastInitTokenErr error  //记录首次初始化token的错误
	APIData          interface{}
	GetData          map[string]interface{}
	AuthActionName   map[string]error //这些路由是需要验证过期的
	beego.Controller
}

// JSONResponse 固定的JSON返回格式结构体
type JSONResponse struct {
	Code    int         `json:"code"`
	Message string      `json:"message"`
	Data    interface{} `json:"data,omitempty"`
}

// Prepare 准备
func (c *BaseController) Prepare() {
	c.InitInput()
}

// Deprecated: JSON 返回JSON信息
func (c *BaseController) JSON(response *JSONResponse) {
	c.Data["json"] = response
	c.ServeJSON()
	c.StopRun()
}

// TraceJSON 返回JSON信息,末尾必加 return
func (c *BaseController) TraceJSON(response *JSONResponse) {
	c.Ctx.Input.SetData(ck_log.RESPCODE, response.Code)
	c.Data["json"] = response
	c.ServeJSON()
}

// RespJson 输出原生json
func (c *BaseController) RespJson(data interface{}) {
	c.Ctx.Output.JSON(data, false, false)
}

// Deprecated: ErrorResp 返回错误信息
func (c *BaseController) ErrorResp(err error, msgs ...string) {
	apiError, ok := err.(*exception.Error)
	if !ok {
		message := err.Error()
		if exception.RunMode == exception.PROD {
			// 正式环境不返回具体的错误信息
			message = "系统错误"
		}
		c.JSON(&JSONResponse{
			Code:    exception.SystemErrorCode,
			Message: message,
		})
		return
	}
	// 测试环境直接输出err
	if exception.RunMode != exception.PROD {
		stack := exception.GetStacks()
		if !ok {
			logs.Error("[response err] err: %s stack: \r\n%s", err, stack)
		}
		//apiError.Message = err.Error()
	}
	if len(msgs) > 0 {
		apiError.Message = msgs[0]
	}
	c.JSON(&JSONResponse{
		Code:    apiError.Code,
		Message: apiError.Message,
	})
}

// TraceErrorResp 返回错误信息 末尾必加 return
func (c *BaseController) TraceErrorResp(err error, msgs ...string) {
	//可见范围修改后特有的判断
	canSeeErr, ok := err.(*exception.CanSeeError)
	if ok {
		message := canSeeErr.Message
		c.TraceJSON(&JSONResponse{
			Code:    exception.AgentCanSeeCode,
			Message: message,
		})
		return
	}

	apiError, ok := err.(*exception.Error)
	if !ok {
		// 以下七行代码为了防止传入nil出错以及传入系统内部err时，可以自定义msg
		message := "服务器开小差了，过会儿重试"
		if err != nil && len(msgs) <= 0 { // 错误信息存在，且未自定义提示信息
			message = err.Error()
		}

		if len(msgs) > 0 { // 自定义提示信息
			message = msgs[0]
		}
		c.TraceJSON(&JSONResponse{
			Code:    exception.SystemErrorCode,
			Message: message,
		})
		return
	}

	if len(msgs) > 0 {
		apiError.Message = msgs[0]
	}
	c.TraceJSON(&JSONResponse{
		Code:    apiError.Code,
		Message: apiError.Message,
	})
}

// TraceErrorDataResp 返回错误信息 末尾必加 return
func (c *BaseController) TraceErrorDataResp(err error, data interface{}, msgs ...string) {
	apiError, ok := err.(*exception.Error)
	if !ok {
		// 以下七行代码为了防止传入nil出错以及传入系统内部err时，可以自定义msg
		message := "服务器开小差了，过会儿重试"
		if err != nil && len(msgs) <= 0 { // 错误信息存在，且未自定义提示信息
			message = err.Error()
		}

		if len(msgs) > 0 { // 自定义提示信息
			message = msgs[0]
		}
		c.TraceJSON(&JSONResponse{
			Code:    exception.SystemErrorCode,
			Message: message,
			Data:    data,
		})
		return
	}

	if len(msgs) > 0 {
		apiError.Message = msgs[0]
	}
	c.TraceJSON(&JSONResponse{
		Code:    apiError.Code,
		Message: apiError.Message,
		Data:    data,
	})
}

// RedirectWithError 302跳转，错误页面
func (c *BaseController) RedirectWithError(redirectURL, errorType string, err error) {
	if redirectURL == "" {
		redirectURL = viper.GetString("url.front") + viper.GetString("wechat.redirect.frontErrorURI")
	}
	apiError, ok := err.(*exception.Error)
	if !ok {
		apiError = &exception.Error{
			Code:    exception.SystemErrorCode,
			Message: err.Error(),
		}
	}
	switch apiError.Code {
	case exception.NoAuthCode:
		errorType = "no_auth"
	case exception.CorpNotAuthCode:
		errorType = "corp_no_auth"
	default:
		errorType = "text"
	}
	//urlStr := common.URLJoin(redirectURL, map[string]string{
	//	"err_type": errorType,
	//	"code":     strconv.Itoa(apiError.Code),
	//	"message":  apiError.Message,
	//})
	//c.Ctx.Redirect(302, urlStr)
	return
}

// SuccessResp 返回成功信息
// - data 需要带入的数据
// - message optional, 成功信息
// Deprecated: 里面关联了JSON
func (c *BaseController) SuccessResp(data interface{}, message ...string) {
	msg := "success"
	if len(message) > 0 {
		msg = message[0]
	}
	c.JSON(&JSONResponse{
		Code:    0,
		Message: msg,
		Data:    data,
	})
}

// TraceSuccessResp 返回成功信息
// - data 需要带入的数据
// - message optional, 成功信息 末尾必加 return
func (c *BaseController) TraceSuccessResp(data interface{}, message ...string) {
	msg := "success"
	if len(message) > 0 {
		msg = message[0]
	}
	c.TraceJSON(&JSONResponse{
		Code:    0,
		Message: msg,
		Data:    data,
	})
}

// Echo 直接输出
func (c *BaseController) Echo(body string, statusCodes ...int) {
	w := c.Ctx.ResponseWriter
	statusCode := http.StatusOK
	if len(statusCodes) > 0 {
		statusCode = statusCodes[0]
	}
	w.WriteHeader(statusCode)
	_, _ = w.Write([]byte(body))
	// c.StopRun()
}

// InitInput 初始化输入内容
func (c *BaseController) InitInput() {
	gds := c.Input()
	if len(gds) > 0 {
		c.GetData = make(map[string]interface{})
		for k, v := range gds {
			if len(v) > 0 {
				c.GetData[k] = v[0]
			}
		}
	}
	if c.Ctx.Request.Method == "POST" {
		var inputFormat interface{}
		_ = json.Unmarshal(c.Ctx.Input.RequestBody, &inputFormat)
		c.APIData = inputFormat
	} else {
		c.APIData = c.GetData
	}
}

// InputFormat 输入格式化
func (c *BaseController) InputFormat() (retInput InputMap) {
	if _, ok := c.APIData.(map[string]interface{}); ok {
		retInput = c.APIData.(map[string]interface{})
	}
	return
}

// GetSize 获取条数参数
func (c *BaseController) GetSize(key string, def int, max int) int {
	size, _ := c.GetInt(key, def)
	if size > max {
		size = max
	}
	return size
}

// GetPage 获取页码
func (c *BaseController) GetPage(key ...string) int {
	keyName := "page"
	if len(key) >= 1 {
		keyName = key[0]
	}
	val, err := c.GetInt(keyName, 1)
	if err != nil {
		val = 1
	}
	return val
}

// GetPageSize 获取条数
func (c *BaseController) GetPageSize(key string, defSize int, maxSize int) (size int) {
	size = defSize
	data := c.InputFormat()
	size = data.GetInt(key, defSize)
	if size > maxSize {
		size = maxSize
	}
	return
}

// GetDefaultPageInfo 构建一个默认的pageInfo结构体
func (c *BaseController) GetDefaultPageInfo() (page, pageSize int, err error) {
	pageSize, err = c.GetInt("size", 10)
	if err != nil {
		err = exception.ParametersInvalid
		return
	}
	page, err = c.GetInt("page", 0)
	if err != nil {
		err = exception.ParametersInvalid
		return
	}
	if pageSize <= 0 {
		pageSize = 10
	}
	if page <= 0 {
		page = 1
	}
	return
}

// CheckToken 校验token
func (c *BaseController) CheckToken() {
	err := c.InitUserToken()
	if err != nil {
		c.ErrorResp(err)
		return
	}
}

// InitUserToken 初始化用户Token
func (c *BaseController) InitUserToken() (err error) {
	if c.IsInitToken {
		return c.LastInitTokenErr
	}
	defer func() {
		// 记录初始化状态
		c.IsInitToken = true
		// 记录初始化错误
		c.LastInitTokenErr = err
	}()

	c.Token, err = c.GetTokenString()
	if err != nil {
		return
	}
	token, err := c.ParseTokenString()
	if err != nil {
		return
	}

	//判断是否已过期或者已登出
	if ok, _ := redis.Redis().Exists(redis2.GetLoginTokenKey(token.UUID, token.RoleID)); ok != 1 {
		err = exception.LoginExpiredError
		return
	}

	err = c.GetUserInfo(c.SysContext(), token.UUID)
	if err != nil {
		return
	}
	////通过uuid获取sc_user信息
	//_, c.UserInfo, err = c.UserInfo.GetWithUUID(c.SysContext(), nil, token.UUID)
	//if err != nil {
	//	return
	//}
	c.UserId = c.UserInfo.ID
	c.SID = c.UserInfo.UserSid
	//if c.UserInfo.Id == 0 {
	//	return exception.AccountNotExistError
	//}

	//if c.UserInfo.Status == modelDb.NrUserStatusIsOff { // 账号关闭
	//	err = errors.New("该账号已关闭")
	//	return
	//}

	err = c.checkUniqueToken()
	return
}

// GetTokenString 获取请求Header中的token
func (c *BaseController) GetTokenString() (tokenString string, err error) {
	if c.ParseToken != "" {
		return c.ParseToken, nil
	}
	// 从Cookie 获取token
	tokenString = strings.Replace(c.Ctx.Input.Cookie("Authorization"), "Bearer ", "", 1)
	if tokenString != "" {
		return
	}
	// 从Header 获取token
	tokenString = strings.Replace(c.Ctx.Input.Header("Authorization"), "Bearer ", "", 1)
	if tokenString != "" {
		return
	}

	// 从Get获取
	tokenString = c.GetString("authorization", "")
	if tokenString == "" {
		err = exception.LoginInvalidError
	}
	return
}

// ParseTokenString 解析tokenString
func (c *BaseController) ParseTokenString() (tokenStruct *models.TokenData, err error) {
	token, _ := jwt.ParseWithClaims(c.Token, jwt.MapClaims{}, func(token *jwt.Token) (interface{}, error) {
		return []byte(viper.GetString("jwt.authCode")), nil
	})
	if token == nil {
		err = exception.LoginInvalidError
		return
	}
	claims, ok := token.Claims.(jwt.MapClaims)
	if !ok || !token.Valid {
		err = exception.LoginInvalidError
		return
	}
	jsonStr, err := json.Marshal(claims)
	if err != nil {
		err = exception.LoginInvalidError
		return
	}
	tokenStruct = new(models.TokenData)
	err = json.Unmarshal(jsonStr, tokenStruct)
	if err != nil {
		err = exception.LoginInvalidError
		return
	}

	if time.Now().Unix() > tokenStruct.ExpireTime {
		err = exception.LoginInvalidError
		return
	}
	if err != nil {
		err = exception.LoginInvalidError
		return
	}
	if len(tokenStruct.UUID) == 0 {
		err = exception.AccountNotExistError
	}
	return
}

//checkUniqueToken 验证 user shop token的唯一性
func (c *BaseController) checkUniqueToken() error {
	return nil
}

func (c *BaseController) isPostJsonRequest() bool {
	return strings.Contains(strings.ToLower(c.Ctx.Input.Header("Content-Type")), "application/json")
}

//ParseStructReq 对结构体进行验证，message标签为错误信息
// type SopCreate struct {
//	 SopName  string `json:"sop_name" validate:"required,lte=20" message:"SOP名称不能为空,SOP名称长度不能大于20"`
// }
func (c *BaseController) ParseStructReq(pointer interface{}) error {
	//请求参数转结构体
	if c.isPostJsonRequest() {
		if err := jsoniter.Unmarshal(c.Ctx.Input.RequestBody, pointer); err != nil {
			//c.ErrorResp(exception.RequestParamError)
			return err
		}
	} else {
		if err := c.ParseForm(pointer); err != nil {
			return err
		}
	}
	//对结构体校验
	if err := ck_validate.Validate.Struct(pointer); err != nil {
		//c.ErrorResp(exception.RequestParamError, err.Error())
		return err
	}
	return nil
}

func (c *BaseController) SysContext(classValue ...string) syscontext.Context {
	sysCtxI := c.Ctx.Input.GetData("ReqContext")
	if sysCtxI != nil {
		sysCtx := sysCtxI.(syscontext.Context)
		initReqContext := c.Ctx.Input.GetData("initReqContext")
		if initReqContext != nil && initReqContext.(string) == "true" {
			return sysCtx
		}
		//sysCtx = syscontext.WithValue(sysCtx, ck_log.GENUS, strconv.Itoa(c.UserID))
		//sysCtx = syscontext.WithValue(sysCtx, ck_log.FAMILY, strconv.Itoa(c.OperateID)) // 实际操作人
		//sysCtx = syscontext.WithValue(sysCtx, ck_log.ORDER, strconv.Itoa(c.CID))
		//if len(classValue) > 0 {
		//	sysCtx = syscontext.WithValue(sysCtx, ck_log.CLASS, classValue[0])
		//	c.Ctx.Input.SetData("initReqContext", "true")
		//}
		c.Ctx.Input.SetData("ReqContext", sysCtx)
		return sysCtx
	}
	return syscontext.Background()
}

func (c *BaseController) SysContextWithClass(classValue string) syscontext.Context {
	return c.SysContext(classValue)
}

// GetUserInfo 获取用户信息
func (c *BaseController) GetUserInfo(ctx syscontext.Context, uuid string) (err error) {
	userJson, _ := redis.Redis().GetString(redis2.Get(redis2.UserInfoKey, uuid))
	if userJson == "" { // 用户信息缓存不存在，重新获取
		exist, user1, err1 := c.UserInfo.GetWithUUID(c.SysContext(), nil, uuid) // 通过uuid获取sc_user信息
		if err1 != nil {
			ck_log.LogCtx(ctx).Errorw("获取用户信息失败", zap.Error(err1))
			err = exception.SystemError
			return
		}

		if !exist { // 账号不存在
			err = exception.AccountNotExistError
			return
		}

		if user1.Status == modelDb.NrUserStatusIsOff { // 账号禁用
			err = exception.AccountsDisabledError
			return
		}

		c.UserInfo = user1
		userByte, err1 := jsoniter.Marshal(user1)
		if err1 != nil {
			ck_log.LogCtx(ctx).Errorw("用户信息序列化失败", zap.Error(err1))
		}

		_, err = redis.Redis().SetString(redis2.Get(redis2.UserInfoKey, uuid), string(userByte), 1800) // 设置缓存
		if err1 != nil {
			ck_log.LogCtx(ctx).Errorw("用户信息设置缓存失败", zap.Error(err1))
		}

		return
	}

	c.UserInfo = modelDb.NewUser()
	err = jsoniter.Unmarshal([]byte(userJson), c.UserInfo)
	if err != nil {
		ck_log.LogCtx(ctx).Errorw("用户缓存信息反序列化失败", zap.Error(err))
		err = exception.SystemError
	}

	return
}
